AngularJS
Some of the guys in the office I work with have been using AngularJS, and Bo Fussing gave a short introduction at codaholics HK, (just before my RooJS introduction). This definatly perked my interest, but I've not really had time to look at in detail to see if it could resolve or improve the solution for this rendering issue.
For those that have not heard of AngularJS, it's basic premise is to convert HTML into a MVC style application, where the HTML is the view, (I guess AngularJS is controlling it all), and you provide a Model (or data), that is to be rendered.
One thing I really liked about AngularJS was the way attributes of HTML are used for things like looping indicators etc., This is very similar to the way HTML_Template_Flexy works. It enable the 'template' to be rendered in a regular HTML editor, and to get a rough preview of the expected output without having to run it through the tool.
You can wander over to the
AngularJS website to get a better idea of what it's all about, however even when I first saw it, I did start to get concerned about some of the design decisions. Then after reviewing the code I decided to give it a miss, for the following reasons.
Code flow.
One of the core premises of Angular is the dynamic updating of rendered layout by just modifying data structures. For example a property in a template {{somevalue}} is linked to someobject.somevalue. if you modify the value of that variable/property, it will update the view (HTML). From my understanding this is done by polling the object, and checking for changes.
This smells like trouble waiting to happen, The normal way to trigger a change in a view would be to call a method, which would then mess arround resolving stuff, and make some changes to the DOM. if anything goes wrong, you could run the debugger and step through the program and see why it was not working. In Angular's logic, you would make a change in one code flow, and you would have to be tracking the update monitoring in the library to understand why events did not occur as expected. I've been bitten enough by this kind of 'threaded' or even fake 'threaded' style application to know it's frequently more trouble than it's worth.
I also have suspicions that monitoring a large data object for changing may have serious performance issues in larger applications. It might work well for your little JQuery quick sexy web effects, but I doubt it will work with large applications.
[UPDATE] As Igor responded the render effect trigger is not polled, but done by wrapping event handlers and redrawing after the event handler returns.
This is definitively an improvement on polling but does involve a little 'magic' that may be done better by forcing the developer to refresh() manually
Readable Source
Before using any kind of library, I pretty much always look at the source code. The reality is that no code is perfect, and when the sh*t hit's the fan, you need to know, can I really dig into this code, within a reasonable timeframe, fix it and deliver a solution without to many issues.
The code for Angular is well documented, lot's of docblock style comments, however that is about where it ends, understanding the mapping of code, classes to files is a complete mess. In RooJS if you need to find the source to Roo.form.ComboBox, look in roojs1/Roo/form/ComboBox.js .. it's that simple.
Angular uses a bundle builder discussed below, what results is code that you read having very little relation to what is actually supplied to the browser. functions and properties defined in the source appear to be public, however are actually made private by the builder), My guess is that you are unlikely to be able to introspect values using the webkit debugger.
[UPDATE] Igor has clarified that the code basically wraps the files in an anonymous function when building.
While this is one of the two approaches available to handle namespace pollution, I personally think this is hugely troublesome in the long run, using a namespace tree has proven to be faster and easier to develop and debug, and is something I would always recommend over this method.
There is also the sense that the developers may have spent too much time looking at JQuery, code like this " attr.$$observers[name] = " is not a sign of quality code. JQuery's primary design was done when bandwidth was limited, and everyone wanted to add a sexy effect to their web page. It was never originally designed for large applications, scaling to solve that problem space just gets messy very quickly.
Building the bundle..
While there is a new extension to Firefox/Chrome comming up that allows you to map compiled javascript code back to the original source, the hastle in setting this up and ensuring it works correctly adds yet another point of failure. AngularJS's 'make' file does quite a considerable amount of munging of the source code. This is the complete opposite philosophy to the RooJS build technique.
In RooJS, the basic principle is that 'debug' build is just all the source code added together in a specific order. The 'compressed' version is pretty similar except it strips whitespace, does variable shortening replacement etc.. But it's simple , KISS...
Stable codebase
One of the biggest complaints about Angular I've heard is the rather loose attitude to BC that they have, although it's claimed to be in RC stage, it appears that breaking any code currently being developed to use it is a common situation, and breaking it in fundimental ways is also occuring.
RooJS, is old and stale in comparison, but breaking existing code is not an option, new features are always added in a way that old stuff is not broken, and if the new way is 'prefered', calling Roo.log() (a portable wrapper to console.log) with a message is about as far as reporting issues go.
Roo.XTemplate
Anyway enough said, Angular while is quite impressive (especially if you play with the examples), The original problem I was trying to solve was rendering the commits for a git repo on a single day by a single user. The backend system can easily return the list of changes, the idea was to play around rendering this as HTML in a panel. (rather than using a classic RooJS grids)
The original javascript code in RooJS is based on ExtJS 1.1, which had a nice LGPL licence and assets from pre-1.0 which had BSD licences. Some of the code that has been ported (eg. Ext changed to Roo) has just been left in the repository without much attention or love.
One of these files was Roo/XTemplate.js, It was originally not documented, and no examples where available to understand what it did. It extends the basic Roo Template class Roo.Template, which does quite trivial replacement of {somevar} in a HTML Snippet and overlays properties of data provided. It does this by creating a javascript function on the fly and calls it to render.
This base class allows you to call methods on the Roo.util.Format class to format the data {somevar:usMoney()}, or methods on the Template using {somevar:this.callsomething()}.
There is another class Roo.MasterTemplate which has support for named templates, however neither support conditional rendering or looping.
So after looking at the Roo.XTemplate code, it became clear that it may do some of these things. The original code in Roo.XTemplate supports these feature using the fake HTML tag <tpl> and depending on what property it will add code to the template to conditionally use this data.
<tpl for="a_variable or condition..">CONTENT</tpl>
<tpl if="a_variable or condition">CONTENT</tpl>
<tpl exec="some javascript">CONTENT</tpl>
There was a number of issues with the existing implementation of XTemplate,
* the original code did not handle universal constructor. (It now does)
- * error handling was pretty much non-existent, if you made an error in the template it would just crash the rendering process...
- It now reports all missing properties using the console.
- * variable where not escaped by default (this is still a flaw with the original Roo.Template code),
- The :raw format method is now used to explicity output raw values, otherwise it is html escaped.
- * It did not support property method calls like format('Y-m-d') on date objects.
- this is hacked in using somevar.format:('Y-m-d');
- * the method for determining undefined properties was flaky at best, value[xxx] === undefined. worked fine on simple tests, however nested properties an methods just broke badly. it now uses 'with(values)' and tests all elements of a nested property (eg. is undefined a, a.b or a.b.c)
- * no support for remote templates. All of the template classes now support {url : 'http://....'} as a constructor argument, if that occurs, it will fetch the template from a remote url, making it easier to maintain.
- * it's regex based, unfortunatly it still is, but there is at least a good base to build a DOM based parser.
So this is the new look of the template usage..
The template
<tpl for="parray">
<li class="change-log-day"><a name="changedate-{changedate.format:('Y-m-d')}"
>{changedate.format:('d / M / Y')}</a>
<tpl if="typeof(first_of_day) != 'undefined' && first_of_day * 1">
FIRST
</tpl>
</li>
</tpl>
The consumer
Roo.onReady(function() {
var tpl = new Roo.XTemplate({
url : baseURL + 'xtemplate-test.html',
});
tpl.append(Roo.get('body'),{
parray : [
{
changedate : new Date()
},
{
changedate : new Date()
}
]
});
});